package elms

import (
	"github.com/traefik/traefik/safe"
	"github.com/traefik/traefik/types"
	"strings"
)

type Application struct {
	types.Backend
	Id      string                     `json:"id,omitempty"`
	Name    string                     `json:"name,omitempty"`
	Server  types.Server               `json:"server,omitempty"`
	Apis    []*Api                     `json:"apis,omitempty"`
	Routers map[string]*types.Frontend `json:"routers,omitempty"`
}

type Api struct {
	Path   string   `json:"path,omitempty"`
	Method string   `json:"method,omitempty"`
	Scopes []string `json:"scopes,omitempty"`
}

func BuildConfiguration(apps []*Application) *types.Configuration {

	config := &types.Configuration{
		Frontends: make(map[string]*types.Frontend),
		Backends:  make(map[string]*types.Backend),
	}

	for _, app := range apps {
		if app == nil {
			continue
		}

		if backend, ok := config.Backends[app.Name]; ok {
			backend.Servers[app.Id] = app.Server
		} else {
			if app.HealthCheck == nil {
				app.HealthCheck = &types.HealthCheck{Path: "/"}
			}

			config.Backends[app.Name] = &types.Backend{
				Servers:        map[string]types.Server{app.Id: app.Server},
				CircuitBreaker: app.CircuitBreaker,
				LoadBalancer:   app.LoadBalancer,
				MaxConn:        app.MaxConn,
				HealthCheck:    app.HealthCheck,
			}

			for _, api := range app.Apis {
				frontend := types.Frontend{
					Backend:        app.Name,
					PassHostHeader: true,
					Routes: map[string]types.Route{
						"route": {
							Rule: "Path:" + api.Path + ";Method:" + api.Method,
						},
					},
				}

				if len(api.Scopes) > 0 {
					frontend.Auth = &types.Auth{
						OAuth2: &types.OAuth2{
							Scopes: api.Scopes,
						},
					}
				}
				config.Frontends[strings.ToLower(api.Method)+":"+api.Path] = &frontend
			}

			for name, router := range app.Routers {
				router.Backend = app.Name
				config.Frontends[name] = router
			}
		}

	}

	return config
}

type Provider struct {
	configurationChan chan<- types.ConfigMessage
}

// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, _ *safe.Pool) error {
	p.configurationChan = configurationChan

	p.AddConfiguration(&types.Configuration{
		Backends: map[string]*types.Backend{
			"traefik": {
				Servers: map[string]types.Server{"traefik": {
					URL: "http://localhost:8080",
				}},
			},
		},
		Frontends: map[string]*types.Frontend{
			"elms-admin-license": {
				Backend:        "traefik",
				PassHostHeader: true,
				Routes: map[string]types.Route{
					"route": {
						Rule: "Path:/api/elms/admin/license",
					},
				},
				Auth: &types.Auth{
					OAuth2: &types.OAuth2{
						Scopes: []string{"admin"},
					},
				},
			},
			"traefik-providers": {
				Backend:        "traefik",
				PassHostHeader: true,
				Routes: map[string]types.Route{
					"route": {
						Rule: "PathPrefix:/api/traefik/providers;ReplacePathRegex: ^/api/traefik/(.*) /api/$1",
					},
				},
				Auth: &types.Auth{
					OAuth2: &types.OAuth2{
						Scopes: []string{"admin"},
					},
				},
			},
		},
	})
	return nil
}

// Init the provider
func (p *Provider) Init(_ types.Constraints) error {
	return nil
}

func (p *Provider) AddConfiguration(configuration *types.Configuration) {
	p.configurationChan <- types.ConfigMessage{
		ProviderName:  "elms",
		Configuration: configuration,
	}
}
